<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<ul class = "list">
    <li>0</li>
    <li>1</li>
    <li>-1</li>
    <li>2</li>
    <li>-2</li>
    <li>3</li>
    <li>-3</li>
</ul>
<script>
    /*
    *
    * 获取这些li中内容大于0的元素形成的数组
    *
    * */

    let liList = document.querySelectorAll(".list li") //liList是一个类数组

    //let resultArr = liList.filter(li => {
    //    return li.innerHTML*1 > 0
    //}) //*1是因为要进行类型判断 统一一下类型好进行比较
    //console.log(resultArr) //没法执行会报错

    //然鹅，filter是数组才有的方法，liList不是一个数组，没有数组的方法
    //虽然它不是数组，但是和数组很相似，我们可以拿数组的方法操作它
    //filter方法通常是谁调用的？通常是.filter前面的东西调用的，要在回调函数中使用这个东西，this是不是应该指向这个调用主体
    //推测：类似于filter方法等所有内容，在函数中的this指向都指向了 调用函数的主体，也就是那个数组
    let filter = [].filter.bind(liList)
    console.log(filter) //bind方法的返回值 返回一个函数 这个返回的被我们声明的filter变量接收
    //随意一个数组去访问它的filter方法，等号右边这是一个函数，我们将这个函数拿到，将函数的调用主体绑定为liList，赋值给左边
    let s = filter(li => li.innerHTML*1 > 0)//如果只有一行，{}和return都可以省略
    console.log(s) //这回就找到了需要找的内容


    let str = "abc"
    //使用数组forEach打印字符串str里面的每一项
    //通过修改调用主体，强行使用别人的方法
    let forEach = [].forEach.bind(str) //将数组方法的调用主体绑定给字符串
    let a = forEach(item => console.log(item))
    console.log(a)

    //通过以上事例可知虽然js的方法并不互通，但是我们可以通过修改调用主体，借用别人的方法的形式解决问题

    /*
    * 以下三个方法都是函数的方法，调用的时候可以修改调用的主体
    * call
    *   函数在调用的时候可以修改它的调用主体
    *   函数.call(调用主体,...传入的参数) //这里的参数是call前面函数方法的参数，例如forEach方法的参数是回调函数，reduce方法的参数也是回调函数
    *   call方法传参的时候，从第二个开始，依次传参，可以接收无数个参数
    *   例如：
    *   let str = "abc"
    *   [].forEach.call(str,function(item){console.log(item)})
    *   //本来forEach是数组的方法，但是通过call将调用主体修改为字符串str，后面的function 是 forEach方法的参数回调函数
    *
    *   let str1 = "12345"
    *   //将字符串的每一项作为数字加起来
    *   //以前我们可能会用这个方法 console.log(str.split("").reduce((t,item)=>item*1+t,0)) //item*1是为了将数组中每项元素转为数字
    *   console.log([].reduce.call(str1,(total,item) => {return total + item*1},0))
    *
    * call方法与bind方法很相似，但是call方法会直接执行，bind方法不会立即执行
    *
    *
    * apply
    *   和call几乎一致，除了传参的时候，apply接收参数的时候，接收的是一个数组
    *   例如将上面的方法改成apply
    *   let str1 = "12345"
    *   console.log([].reduce.apply(str1,[(total,item) => {return total + item*1},0])) //这里将参数回调函数用数组装载
    *
    *
    * bind
    *   相当于提前准备好函数加工材料，等待下达指令执行
    *   bind的返回值是返回一个函数
    *
    *   bind方法的优点：可以分两次传参数
    * */

    //function sum(){ //不定参数传参
    //    return arguments.reduce((t,i) => {return t+i})
    //}
    //console.log(sum(1,2,3)) //会报错，因为arguments是类数组
    //应该改成：
    function sum1(){ //不定参数求和
        return [].reduce.call(arguments,(t,i) => {return t + i}) //数组的方法绑定给arguments
    }
    console.log(sum1(1,2,3))
    //假如平时成绩加期末成绩等于最终成绩
    //假设summer平时成绩45，期末成绩还没出，该怎么传
    let summer = sum1.bind(null,45)//前面期末成绩，后面平时成绩 这里因为等号后面的bind返回值会返回一个函数，所以这个summer其实是一个函数
    console.log(summer(46)) //summer要执行的时候再传入期末成绩


</script>
</body>
</html>